package yc.star.system.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import yc.star.common.constants.Constant;
import yc.star.common.domain.AppResult;
import yc.star.common.security.exception.ApplicationException;
import yc.star.system.domain.exam.Exam;
import yc.star.system.domain.exam.ExamQuestion;
import yc.star.system.domain.exam.dto.ExamAddDTO;
import yc.star.system.domain.exam.dto.ExamEditDTO;
import yc.star.system.domain.exam.dto.ExamQueryDTO;
import yc.star.system.domain.exam.dto.ExamQuestionAddDTO;
import yc.star.system.domain.exam.vo.ExamDetailVO;
import yc.star.system.domain.exam.vo.ExamVO;
import yc.star.system.domain.question.Question;
import yc.star.system.domain.question.vo.QuestionVO;
import yc.star.system.manager.ExamCacheManager;
import yc.star.system.mapper.ExamMapper;
import yc.star.system.mapper.ExamQuestionMapper;
import yc.star.system.mapper.QuestionMapper;
import yc.star.system.service.IExamService;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
public class ExamServiceImpl extends ServiceImpl<ExamQuestionMapper,ExamQuestion> implements IExamService {

    @Autowired
    private ExamMapper examMapper;
    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private ExamQuestionMapper examQuestionMapper;
    @Autowired
    private ExamCacheManager examCacheManager;

    @Override
    public List<ExamVO> list(ExamQueryDTO examQueryDTO) {
        PageHelper.startPage(examQueryDTO.getPageNum(),examQueryDTO.getPageSize());
        List<ExamVO> examVOList = examMapper.selectExamList(examQueryDTO);
        return examVOList;
    }

    @Override
    public long add(ExamAddDTO examAddDTO) {
        if (examAddDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ApplicationException(AppResult.failed("竞赛开始时间不能早于当前时间"));
        }
        if (examAddDTO.getStartTime().isAfter(examAddDTO.getEndTime())) {
            throw new ApplicationException(AppResult.failed("竞赛开始时间不能晚于结束时间"));
        }
        List<Exam> exams = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle,examAddDTO.getTitle()));
        if (CollectionUtil.isNotEmpty(exams)) {
            throw new ApplicationException(AppResult.failed("该题目已存在"));
        }
        Exam addExam = new Exam();
        BeanUtil.copyProperties(examAddDTO,addExam);
        // 将竞赛基本信息插入到数据库
        examMapper.insert(addExam);
        // 给前端返回竞赛id
        return addExam.getExamId();
    }

    @Override
    public boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {
        Exam existExam = examMapper.selectById(examQuestionAddDTO.getExamId());
        Set<Long> questionIdList = examQuestionAddDTO.getQuestionIdSet();
        // 判断竞赛是否存在
        if (existExam == null) {
            throw new ApplicationException(AppResult.failed("该竞赛不存在"));
        }
        // 判断id集合是否为空
        if (CollectionUtil.isEmpty(questionIdList)) {
            return true;
        }

        // 判断题目是否都是存在的
        List<Question> questions = questionMapper.selectBatchIds(questionIdList);
        if (CollectionUtil.isEmpty(questions) || questions.size() < questionIdList.size()) {
            throw new ApplicationException(AppResult.failed("题目不存在或已被删除"));
        }

        int num = 1; // 定义题目顺序
        // 定义一个插入数据的集合
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for (Long questionId : questionIdList) {
            // 将每条题目存入到竞赛当中
            ExamQuestion addExamQuestion = new ExamQuestion();
            addExamQuestion.setExamId(existExam.getExamId());
            addExamQuestion.setQuestionId(questionId);
            addExamQuestion.setQuestionOrder(num++);
            // 插入到集合当中
            examQuestionList.add(addExamQuestion);
        }
        return saveBatch(examQuestionList);
    }

    @Override
    public ExamDetailVO detail(@NonNull Long examId) {
        // 查询竞赛信息
        Exam exam = examMapper.selectById(examId);
        // 判断竞赛是否存在
        if (exam == null) {
            throw new ApplicationException(AppResult.failed("该竞赛不存在"));
        }
        ExamDetailVO examDetailVO = new ExamDetailVO();
        BeanUtil.copyProperties(exam,examDetailVO);
        // 查询竞赛中的题目信息
        List<ExamQuestion> examQuestionList = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion::getQuestionId)
                .eq(ExamQuestion::getExamId, examId)
                .orderByAsc(ExamQuestion::getQuestionOrder));
        // 如果竞赛中没有题目
        if (CollectionUtil.isEmpty(examQuestionList)) {
            return examDetailVO;
        }
        // 将竞赛题目集合中的题目id都拿出来
        List<Long> questionIdList = examQuestionList.stream().map(ExamQuestion::getQuestionId).toList();
        // 将所有的题目从数据库中查询出来
        List<Question> questionList = questionMapper.selectList(new LambdaQueryWrapper<Question>()
                .select(Question::getQuestionId,Question::getTitle,Question::getDifficulty)
                .in(Question::getQuestionId, questionIdList));
        List<QuestionVO> questionVOList = BeanUtil.copyToList(questionList, QuestionVO.class);
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        // 参数判断
        if (examEditDTO.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ApplicationException(AppResult.failed("竞赛开始时间不能早于当前时间"));
        }
        if (examEditDTO.getStartTime().isAfter(examEditDTO.getEndTime())) {
            throw new ApplicationException(AppResult.failed("竞赛开始时间不能晚于结束时间"));
        }
        List<Exam> exams = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .eq(Exam::getTitle,examEditDTO.getTitle())
                .ne(Exam::getExamId,examEditDTO.getExamId()));
        if (CollectionUtil.isNotEmpty(exams)) {
            throw new ApplicationException(AppResult.failed("该题目已存在"));
        }
        // 查询竞赛信息
        Exam exam = examMapper.selectById(examEditDTO.getExamId());
        // 判断竞赛是否存在
        if (exam == null) {
            throw new ApplicationException(AppResult.failed("该竞赛不存在"));
        }
        // 判断是否能进行操作
        if (Constant.TRUE.equals(exam.getStatus())) {
            throw new ApplicationException("竞赛已发布，不能进行编辑删除操作");
        }
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ApplicationException(AppResult.failed("比赛已开始不能进行操作"));
        }
        // 往数据库中更新数据
        exam.setTitle(examEditDTO.getTitle());
        exam.setStartTime(examEditDTO.getStartTime());
        exam.setEndTime(examEditDTO.getEndTime());
        return examMapper.updateById(exam);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        // 查询竞赛信息
        Exam exam = examMapper.selectById(examId);
        // 判断竞赛是否存在
        if (exam == null) {
            throw new ApplicationException(AppResult.failed("该竞赛不存在"));
        }
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
             throw new ApplicationException(AppResult.failed("比赛已开始不能进行操作"));
        }
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId)
                .eq(ExamQuestion::getQuestionId,questionId));
    }

    @Override
    public int delete(Long examId) {
        // 查询竞赛信息
        Exam exam = examMapper.selectById(examId);
        // 判断竞赛是否存在
        if (exam == null) {
            throw new ApplicationException(AppResult.failed("该竞赛不存在"));
        }
        // 判断是否可以操作
        if (Constant.TRUE.equals(exam.getStatus())) {
            throw new ApplicationException("竞赛已发布，不能进行编辑删除操作");
        }
        if (exam.getStartTime().isBefore(LocalDateTime.now()) && exam.getEndTime().isAfter(LocalDateTime.now())) {
            throw new ApplicationException(AppResult.failed("比赛已开始不能进行操作"));
        }
        // 将竞赛信息给删除
        examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId,examId));
        return examMapper.deleteById(exam);
    }

    @Override
    public int publish(Long examId) {
        // 查询竞赛信息
        Exam exam = examMapper.selectById(examId);
        // 判断竞赛是否存在
        if (exam == null) {
            throw new ApplicationException(AppResult.failed("该竞赛不存在"));
        }
        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ApplicationException(AppResult.failed("该竞赛已结束不允许发布"));
        }
        // 判断竞赛中的题目是否为空
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId));
        if (count == null || count <= 0) {
            throw new ApplicationException(AppResult.failed("该竞赛中没有题目不允许发布"));
        }
        exam.setStatus(Constant.TRUE);
        // 将新发布的竞赛存储到redis中
        examCacheManager.addCache(exam);
        // 将数据库中的状态更新
        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        // 查询竞赛信息
        Exam exam = examMapper.selectById(examId);
        // 判断竞赛是否存在
        if (exam == null) {
            throw new ApplicationException(AppResult.failed("该竞赛不存在"));
        }
        // 判断竞赛是否开始
        if (exam.getStartTime().isBefore(LocalDateTime.now())) {
            throw new ApplicationException(AppResult.failed("竞赛已开始不允许操作"));
        }
        // 判断竞赛是否结束
        if (exam.getEndTime().isBefore(LocalDateTime.now())) {
            throw new ApplicationException(AppResult.failed("该竞赛已结束不允许发布"));
        }
        exam.setStatus(Constant.FALSE);
        examCacheManager.deleteCache(exam.getExamId());
        return examMapper.updateById(exam);
    }

}